home *** CD-ROM | disk | FTP | other *** search
- #import "Newsgroup.h"
- #import "Article.h"
- #import "NNTP.h"
- #import "Interval.h"
- #import "ColumnMatrix.h"
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #import "GrayCell.h"
- #import "descriptors.h"
- #import <ctype.h>
- #import "response_codes.h"
- #import "rfc822realname.h"
- #import "KillFile.h"
- #import "Alexandra.h"
-
- static int sortType=SORT_BY_NAME;
-
- @implementation Newsgroup
-
- + setSortType:(int)t
- {
- sortType=t;
-
- return self;
- }
-
- + (int)sortType
- {
- return sortType;
- }
-
- - initTextCell:(const char *)aString
- {
- subscribed=FALSE;
- [super initTextCell:aString];
- [self setWrap:NO];
- numUnreadArticles=0;
- articleList=[[MiscSortedList alloc] init];
- [articleList setSortEnabled:FALSE];
- [articleList setSortOrder:Misc_ASCENDING];
-
- intervalList=[[List alloc] init];
- delayed=TRUE;
- min=0;
- max=-1;
- first_unread=0;
-
- return self;
- }
-
- - updateArticles
- {
- int i,j=[articleList count];
- for(i=0;i<j;i++)
- [[articleList objectAt:i] composeTitle];
-
- return self;
- }
-
- - free
- {
- if(articleList!=nil){
- [articleList makeObjectsPerform:@selector(free)];
- [articleList free];
- }
- if(intervalList!=nil){
- [intervalList makeObjectsPerform:@selector(free)];
- [intervalList free];
- }
- [super free];
- return self;
- }
-
- - setSubscribed
- {
- subscribed=TRUE;
- return self;
- }
-
- - setUnsubscribed
- {
- subscribed=FALSE;
- return self;
- }
-
- - (BOOL)isSubscribed
- {
- return subscribed;
- }
-
- - setNumUnreadArticles:(int)num
- {
- numUnreadArticles=(unsigned)num;
- return self;
- }
-
- - incNumberUnreadArticles:(int)delta
- {
- numUnreadArticles+=delta;
- return self;
- }
-
- - approxNumUnread
- {
- long i;
-
- numUnreadArticles=0;
- if((min==0)&&(max==0))
- return self;
- for(i=min;i<=max;i++)
- if([self inReadList:i]==FALSE)
- numUnreadArticles++;
- return self;
- }
-
- - (long)numberUnreadArticles
- {
- return numUnreadArticles;
- }
-
- - (int)markAllReadUntil:lastArticle
- {
- int i,j,ii=0;
- Article *anArticle;
-
- if(delayed==TRUE){
- if(intervalList!=nil){
- [intervalList makeObjectsPerform:@selector(free)];
- [intervalList empty];
- }
- else
- intervalList=[[List alloc] init];
- if(min<=max)
- [intervalList addObject:[[Interval alloc] initWithLower:min upper:max]];
- [self setNumUnreadArticles:0];
- }
- else{
- for(i=0,j=[articleList count];i<j;i++){
- anArticle=[articleList objectAt:i];
- if([anArticle isRead]==FALSE){
- ii++;
- [anArticle setRead];
- }
- if(anArticle==lastArticle)
- break;
- }
- if(ii>0)
- [self incNumberUnreadArticles:(-1)*ii];
- }
-
- return ii;
- }
-
- - setTextAttributes:textObj
- {
- [super setTextAttributes:textObj];
- if (subscribed==FALSE)
- [textObj setTextGray:NX_DKGRAY];
- else
- [textObj setTextGray:NX_BLACK];
- return textObj;
- }
-
- - drawInside:(const NXRect *)cellFrame inView:controlView
- {
- NXRect numrect;
- static id sharedTextCell = nil;
- char numstr[40];
-
- //erase cell
- PSsetgray((cFlags1.state || cFlags1.highlighted) ? NX_WHITE : NX_LTGRAY);
- NXRectFill(cellFrame);
-
- //make cell
- if (!sharedTextCell) {
- sharedTextCell = [[GrayCell alloc] init];
- [sharedTextCell setWrap:NO];
- }
-
- // draw text
- if(subscribed==TRUE)
- [sharedTextCell setDrawGray:FALSE];
- else
- [sharedTextCell setDrawGray:TRUE];
- [sharedTextCell setFont:[self font]];
- [sharedTextCell setStringValueNoCopy:[self stringValue]];
- [sharedTextCell drawInside:cellFrame inView:controlView];
-
- // draw number unread articles to the right
- if(numUnreadArticles!=0){
- sprintf(numstr,"%d",numUnreadArticles);
- [sharedTextCell setStringValue:numstr];
- NX_WIDTH(&numrect)=[[sharedTextCell font] getWidthOf:numstr]+4.0;
- NX_HEIGHT(&numrect)=NX_HEIGHT(cellFrame);
- NX_X(&numrect)=NX_X(cellFrame)+NX_WIDTH(cellFrame)- NX_WIDTH(&numrect);
- NX_Y(&numrect)=NX_Y(cellFrame);
- PSsetgray((cFlags1.state || cFlags1.highlighted) ? NX_WHITE : NX_LTGRAY);
- NXRectFill(&numrect);
- [sharedTextCell drawInside:&numrect inView:controlView];
- }
-
- PSsetgray(NX_DKGRAY);
- if (cFlags1.state || cFlags1.highlighted){
- NXRect rectArray[2];
-
- NXSetRect(&(rectArray[0]),NX_X(cellFrame),NX_Y(cellFrame), NX_WIDTH(cellFrame),1);
- NXSetRect(&(rectArray[1]),NX_X(cellFrame),NX_MAXY(cellFrame)-1,
- NX_WIDTH(cellFrame), 1.0);
- NXRectFillList(rectArray, 2);
- }
-
- return self;
- }
-
- - highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag
- {
- if(cFlags1.highlighted != flag){
- cFlags1.highlighted = flag;
- [self drawInside:cellFrame inView:controlView];
- }
- return self;
- }
-
- - setReadList:(char *)readlist
- {
- int x,y,i;
- long lower,upper;
-
- if(intervalList==nil)
- intervalList=[[List alloc] init];
- y=strlen(readlist);
- if((y>0)&&(readlist[y-1]=='\n')){
- readlist[y-1]='\0';
- y--;
- }
- i=0;
- while(i<y){
- x=i;
- while((i<y)&&(isdigit(readlist[i])!=0))
- i++;
- if(isdigit(readlist[x])!=0)
- lower=atol(readlist+x);
- else
- lower=0;
- if((i==y)||(readlist[i]==',')){
- upper=lower;
- i++;
- }
- else if(readlist[i]=='-'){
- x=++i;
- while((i<y)&&(isdigit(readlist[i])!=0))
- i++;
- if(isdigit(readlist[x])!=0)
- upper=atol(readlist+x);
- else
- upper=0;
- if(readlist[i]==',')
- i++;
- }
- else{
- i++;
- continue;
- }
- if((upper!=0) && (lower!=0))
- [intervalList addObject:[[Interval alloc] initWithLower:lower upper:upper]];
- }
-
- if([intervalList count]>0 && [NXApp defaultBoolValue:DEFAULT_PRIVATE_EXPIRE])
- first_unread=[[intervalList objectAt:0] upperBound]+1;
-
- return self;
- }
-
- - (BOOL)inReadList:(long)number
- {
- int i,ii;
-
- ii=[intervalList count];
- for(i=0;i<ii;i++)
- if([[intervalList objectAt:i] isIn:number]==TRUE)
- return TRUE;
- return FALSE;
- }
-
- - dumpReadList:(NXStream *)aStream
- {
-
- long smallest,largest;
- BOOL run_started;
- long start,stop;
- long i,j;
- char *bucket;
- long bsize;
- long c=0;
-
- //ever scanned this newsgroup?
- if(delayed==TRUE){
- j=[intervalList count];
- for(i=0;i<j;i++){
- [[intervalList objectAt:i] dumpAsAsciiIn:aStream];
- if(i<j-1)
- NXWrite(aStream,",",1);
- }
- return self;
- }
-
- // no articles in this group?
- j=[articleList count];
- if(j==0){
- if(min<max)
- NXPrintf(aStream,"1-%d",max);
- return self;
- }
-
- //let's start -- first get smallest and largest article number
- smallest=[[articleList objectAt:0] number];
- largest=smallest;
- for(i=0;i<j;i++){
- long num=[[articleList objectAt:i] number];
- if(num<smallest) smallest=num;
- if(num>largest) largest=num;
- }
-
- //malloc bucket and fill it
- bsize=largest-smallest+2;
- bucket=(char *)calloc(bsize,sizeof(char));
-
- for(i=0;i<j;i++){
- id aCell=[articleList objectAt:i];
- if([aCell isRead]==FALSE)
- bucket[[aCell number]-smallest]=(char)1;
- }
- bucket[bsize-1]=(char)1; //dummy!!!
-
- //init
- run_started=FALSE;
- start=1;
- stop=0;
- if(smallest!=1){
- run_started=TRUE;
- stop=smallest-1;
- }
-
- for(i=0;i<bsize;i++){
- if(run_started==TRUE){
- if(bucket[i]==(char)1){
- if(start<=stop)
- if(c>0)
- NXPrintf(aStream,",");
- else c++;
- if(start<stop)
- NXPrintf(aStream,"%d-%d",start,stop);
- else
- if(start==stop)
- NXPrintf(aStream,"%d",start);
- run_started=FALSE;
- }
- else // bucket==0
- stop++;
- }
- else
- if(bucket[i]==(char)0){
- start=i+smallest;
- stop=start;
- run_started=TRUE;
- }
- }
-
- free(bucket);
-
- return self;
- }
-
- - scanArticles:(NNTP *)nntpServer
- {
- return [self scanArticles:nntpServer visibleIn:nil];
- }
-
- - scanArticles:(NNTP *)nntpServer visibleIn:articleView
- {
- long oldMin,oldMax;
- long from,to,expired_min=0;
- Article *anArticle;
- int i,j,ii;
- Storage *array;
- subjectDesc *subDesc;
- headerDesc *h;
- char buf[512];
-
- if([NXApp defaultBoolValue:DEFAULT_PRIVATE_EXPIRE]){
- float a=(float)[NXApp defaultIntValue:DEFAULT_EXPIRE_RELATIV_OFFSET]/100;
- int b=[NXApp defaultIntValue:DEFAULT_EXPIRE_ABSOLUTE_OFFSET];
- long y1,y2,y1cnt=first_unread-min;
- y1= (y1cnt < 1)? min : (long)(first_unread - a*y1cnt);
- y1cnt=first_unread-b;
- y2= y1cnt<0 ? min : y1cnt;
- expired_min=MAX(y1,y2);
- }
-
- oldMin=-1; oldMax=-1;
- from=-1; to=-1;
- j=[articleList count];
- if((delayed==TRUE)||(j==0)){
- if(expired_min>0)
- from=expired_min;
- else
- from=min;
- to=max;
- }
- else{
- oldMin=[[articleList objectAt:0] number];
- oldMax=oldMin;
- // find min + max
- for(i=0;i<j;i++){
- long num=[[articleList objectAt:i] number];
- if(num>oldMax) oldMax=num;
- if(num<oldMin) oldMin=num;
- }
-
- // see if there's something to do
- if(max>oldMax){
- from=oldMax+1;
- to=max;
- }
- }
-
-
-
- // get new articles?
- if(from!=-1){
- array=[[Storage alloc] initCount:0 elementSize:sizeof(subjectDesc) description:"{l*}"];
-
- [nntpServer fetchSubjectHeaders:array from:from to:to];
-
- j=[array count];
- for(i=0;i<j;i++){
- subDesc=(subjectDesc *)[array elementAt:(unsigned int)i];
-
- //Assert a Subject header
- if(!subDesc->fieldBody[SUBJECT])
- subDesc->fieldBody[SUBJECT]=NXCopyStringBuffer("(none)");
- else if(!(*subDesc->fieldBody[SUBJECT])){
- free(subDesc->fieldBody[SUBJECT]);
- subDesc->fieldBody[SUBJECT]=NXCopyStringBuffer("(none)");
- }
-
- anArticle=[[Article alloc] initWithNumber:subDesc->number];
- h=[anArticle header];
- for(ii=0;ii<XOVER_COUNT;ii++)
- h->fieldBody[ii]=subDesc->fieldBody[ii];
- free(subDesc->fieldBody);
- [anArticle setSize:subDesc->artsize];
- [anArticle setLines:subDesc->lines];
- [anArticle composeTitle];
-
- if([self inReadList:subDesc->number]==TRUE)
- [[anArticle setRead] unsetTag];
- else
- [[anArticle setUnread] setTag];
- [articleList addObject:anArticle];
- }
- [array free];
- }
-
- // see if there are any expired articles
- if((delayed==FALSE) && (oldMin!=-1) && (min>oldMin))
- for(i=[articleList count]-1;i>=0;i--)
- if([[articleList objectAt:i] number]<min){
- id bogusCell=[articleList objectAt:i];
- if(articleView!=nil)
- [articleView removeInvalidCell:bogusCell andUpdate:FALSE];
- else
- [[articleList removeObjectAt:i] free];
- }
- //get sort order
- sprintf(buf,"SortType %s",[nntpServer serverName]);
- [self resortIfNeeded:[NXApp defaultIntValue:buf]];
-
- //filter articles
- [[nntpServer killFile] filterArticles:self andReloadMatrix:FALSE];
-
- // calc number unread articles
- numUnreadArticles=0;
- j=[articleList count];
- for(i=0;i<j;i++)
- if([[articleList objectAt:i] isRead]==FALSE)
- numUnreadArticles++;
-
- delayed=FALSE;
- return self;
- }
-
- - (BOOL)isDelayed
- {
- return delayed;
- }
-
- - (List *)articleList
- {
- return articleList;
- }
- - unsetTag
- {
- myTag=0;
- return self;
- }
-
- - setTag
- {
- myTag=1;
- return self;
- }
-
- - (BOOL)isTaged
- {
- return(myTag==1);
- }
-
- - setMin:(long)newMin
- {
- min=newMin;
-
- return self;
- }
-
- - setMax:(long)newMax;
- {
- max=newMax;
- return self;
- }
-
- - (long)minNumber
- {
- return min;
- }
-
- - (long)maxNumber
- {
- return max;
- }
-
- - (BOOL)bogus
- {
- return isBogus;
- }
-
- - setBogus:(BOOL)b
- {
- isBogus=b;
- return self;
- }
-
- - setPostable:(char)p
- {
- postable=p;
- return self;
- }
-
- - (char)postable
- {
- return postable;
- }
-
- - (BOOL)resortIfNeeded:(int)val
- {
- if(![articleList sorted]||(val!=currentSortType)){
- currentSortType=val;
- [Article setSortType:val];
- [articleList unsort];
- [articleList sort];
- return TRUE;
- }
- return FALSE;
- }
-
- //MiscCompare protocol implementation
-
- - (int)compare:anObject
- {
- BOOL t;
- int s;
-
- if(anObject==nil) return 0;
- if(sortType==SORT_BY_NAME)
- return(strcmp([self stringValue],[anObject stringValue]));
-
- t=[anObject isTaged];
- if(t!=[self isTaged])
- return (t? 1 : -1);
-
- if(t){
- s=[anObject state];
- if(s!=[self state])
- return (s==1? 1 : -1);
- }
-
- t=[anObject isSubscribed];
- if(t!=[self isSubscribed])
- return(t? 1 : -1);
-
- t=[anObject isDelayed];
- if(t!=[self isDelayed])
- return(t? 1 : -1);
-
- return 0;
- }
-
-
-
- @end
-